using Gj.ILRuntimeExt.Adapt;
using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Intepreter;
using ILRuntime.Runtime.Stack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ILRuntime.CLR.TypeSystem;
using SimpleJSON;
using UnityEngine;

namespace Gj.ILRuntimeExt
{
    public static class Register
    {
        static Register()
        {
        }

        public static void Init()
        {
            // 进行ILRuntime初始化配置
            Projects.OnInitialize += OnInitialize;
        }

        public unsafe static void OnInitialize()
        {
            // 同一个参数组合的委托，只需要注册一次即可
            Projects.Domain.DelegateManager.RegisterMethodDelegate<int, float>();
            // 带参数委托
            Projects.Domain.DelegateManager.RegisterFunctionDelegate<int, float, bool>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<int>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<bool>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<Dictionary<string, object>>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<string>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<object>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<JSONObject>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<JSONArray>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<int, JSONArray>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<object[]>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<List<Skill>>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<Gj.Galaxy.Logic.GamePlayer>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<Gj.Galaxy.Logic.GamePlayer, object>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<int, System.Action<SimpleJSON.JSONArray>>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<System.Int32, System.Action<System.Object[]>>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<System.String, System.String, System.String>();
            Projects.Domain.DelegateManager.RegisterFunctionDelegate<System.Object[], System.Object[], System.Boolean>();
            Projects.Domain.DelegateManager.RegisterFunctionDelegate<SimpleJSON.JSONObject, SimpleJSON.JSONObject, System.Int32>();
            Projects.Domain.DelegateManager.RegisterFunctionDelegate<Gj.Galaxy.Logic.GamePlayer, System.Threading.Tasks.Task>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<System.Int32, System.Int32>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<System.Single>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<System.String, System.Int32>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<System.Int32, System.String>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<System.String, System.String>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<System.Object[], System.String>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<Gj.Galaxy.Logic.TeamPlayer, System.Object[]>();
            Projects.Domain.DelegateManager.RegisterMethodDelegate<System.String, System.Object>();
            Projects.Domain.DelegateManager.RegisterFunctionDelegate<SimpleJSON.JSONNode, SimpleJSON.JSONNode, System.Int32>();
            Projects.Domain.DelegateManager.RegisterFunctionDelegate<System.Object[], SimpleJSON.JSONObject>();
            Projects.Domain.DelegateManager.RegisterDelegateConvertor<Gj.Galaxy.Logic.ConnectManager.OnPlayerInitDelegate>((act) =>
            {
                return new Gj.Galaxy.Logic.ConnectManager.OnPlayerInitDelegate((player) =>
                {
                    ((Action<Gj.Galaxy.Logic.GamePlayer>)act)(player);
                });
            });
            Projects.Domain.DelegateManager.RegisterDelegateConvertor<System.Comparison<SimpleJSON.JSONObject>>((act) =>
            {
                return new System.Comparison<SimpleJSON.JSONObject>((x, y) =>
                {
                    return ((Func<SimpleJSON.JSONObject, SimpleJSON.JSONObject, System.Int32>)act)(x, y);
                });
            });

            Projects.Domain.DelegateManager.RegisterDelegateConvertor<Gj.Galaxy.Logic.ConnectManager.OnErrorDelegate>((act) =>
            {
                return new Gj.Galaxy.Logic.ConnectManager.OnErrorDelegate((message) =>
                {
                    ((Action<String>)act)(message);
                });
            });
            Projects.Domain.DelegateManager.RegisterDelegateConvertor<Gj.Galaxy.Logic.ConnectManager.OnPlayerInitTaskDelegate>((act) =>
               {
                   return new Gj.Galaxy.Logic.ConnectManager.OnPlayerInitTaskDelegate((player) =>
                   {
                       return ((Func<Gj.Galaxy.Logic.GamePlayer, System.Threading.Tasks.Task>)act)(player);
                   });
               });
            Projects.Domain.DelegateManager.RegisterDelegateConvertor<Gj.Galaxy.Logic.ConnectManager.OnTeamChangeInfoDelegate>((act) =>
            {
                return new Gj.Galaxy.Logic.ConnectManager.OnTeamChangeInfoDelegate((objs) =>
                {
                    return ((Func<object[], JSONObject>)act)(objs);
                });
            });
            Projects.Domain.DelegateManager.RegisterDelegateConvertor<Gj.Galaxy.Logic.ConnectManager.OnTeamChangeInfoPlayerDelegate>((act) =>
            {
                return new Gj.Galaxy.Logic.ConnectManager.OnTeamChangeInfoPlayerDelegate((player, objs) =>
                {
                    ((Action<Gj.Galaxy.Logic.TeamPlayer, object[]>)act)(player, objs);
                });
            });
            Projects.Domain.DelegateManager.RegisterDelegateConvertor<System.Comparison<SimpleJSON.JSONNode>>((act) =>
            {
                return new System.Comparison<SimpleJSON.JSONNode>((x, y) =>
                {
                    return ((Func<SimpleJSON.JSONNode, SimpleJSON.JSONNode, System.Int32>)act)(x, y);
                });
            });
            // 跨域继承
            Projects.Domain.RegisterCrossBindingAdaptor(new MonoBehaviourAdapter());
            Projects.Domain.RegisterCrossBindingAdaptor(new NamespaceListenerAdaptor());
            Projects.Domain.RegisterCrossBindingAdaptor(new NodeComponentAdapter());
            Projects.Domain.RegisterCrossBindingAdaptor(new NodePageAdapter());
            Projects.Domain.RegisterCrossBindingAdaptor(new GameAreaListenerAdaptor());
            Projects.Domain.RegisterCrossBindingAdaptor(new GameBeforeListenerAdaptor());
            
            // CLR重定向
            var arr = typeof(GameObject).GetMethods();
            foreach (var i in arr)
            {
                if (i.Name == "GetComponent" && i.GetGenericArguments().Length == 1)
                {
                    Projects.Domain.RegisterCLRMethodRedirection(i, GetComponent);
                }
            }
            arr = typeof(GameObject).GetMethods();
            foreach (var i in arr)
            {
                if (i.Name == "AddComponent" && i.GetGenericArguments().Length == 1)
                {
                    Projects.Domain.RegisterCLRMethodRedirection(i, AddComponent);
                }
            }
        }
        
        public unsafe static StackObject* CreateInstance(ILIntepreter intp, StackObject* esp, List<object> mStack, CLRMethod method, bool isNewObj)
        {
            //获取泛型参数<T>的实际类型
            IType[] genericArguments = method.GenericArguments;
            if (genericArguments != null && genericArguments.Length == 1)
            {
                var t = genericArguments[0];
                if (t is ILType)//如果T是热更DLL里的类型
                {
                    //通过ILRuntime的接口来创建实例
                    return ILIntepreter.PushObject(esp, mStack, ((ILType)t).Instantiate());
                }
                else
                    return ILIntepreter.PushObject(esp, mStack, Activator.CreateInstance(t.TypeForCLR));//通过系统反射接口创建实例
            }
            else
                throw new EntryPointNotFoundException();
        }

        public unsafe static StackObject* DLog(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
        {
            ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
            StackObject* ptr_of_this_method;
            //只有一个参数，所以返回指针就是当前栈指针ESP - 1
            StackObject* __ret = ILIntepreter.Minus(__esp, 1);
            //第一个参数为ESP -1， 第二个参数为ESP - 2，以此类推
            ptr_of_this_method = ILIntepreter.Minus(__esp, 1);
            //获取参数message的值
            object message = StackObject.ToObject(ptr_of_this_method, __domain, __mStack);
            //需要清理堆栈
            __intp.Free(ptr_of_this_method);
            //如果参数类型是基础类型，例如int，可以直接通过int param = ptr_of_this_method->Value获取值，
            //关于具体原理和其他基础类型如何获取，请参考ILRuntime实现原理的文档。

            //通过ILRuntime的Debug接口获取调用热更DLL的堆栈
            string stackTrace = __domain.DebugService.GetStackTrance(__intp);
            UnityEngine.Debug.Log(string.Format("{0}\n{1}", message, stackTrace));

            return __ret;
        }
        

    unsafe static StackObject* AddComponent(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
    {
        //CLR重定向的说明请看相关文档和教程，这里不多做解释
        ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;

        var ptr = __esp - 1;
        //成员方法的第一个参数为this
        GameObject instance = StackObject.ToObject(ptr, __domain, __mStack) as GameObject;
        if (instance == null)
            throw new System.NullReferenceException();
        __intp.Free(ptr);

        var genericArgument = __method.GenericArguments;
        //AddComponent应该有且只有1个泛型参数
        if (genericArgument != null && genericArgument.Length == 1)
        {
            var type = genericArgument[0];
            object res;
            if(type is CLRType)
            {
                //Unity主工程的类不需要任何特殊处理，直接调用Unity接口
                res = instance.AddComponent(type.TypeForCLR);
            }
            else
            {
                //热更DLL内的类型比较麻烦。首先我们得自己手动创建实例
                var ilInstance = new ILTypeInstance(type as ILType, false);//手动创建实例是因为默认方式会new MonoBehaviour，这在Unity里不允许
                //接下来创建Adapter实例
                var clrInstance = instance.AddComponent<MonoBehaviourAdapter.Adaptor>();
                //unity创建的实例并没有热更DLL里面的实例，所以需要手动赋值
                clrInstance.ILInstance = ilInstance;
                clrInstance.AppDomain = __domain;
                //这个实例默认创建的CLRInstance不是通过AddComponent出来的有效实例，所以得手动替换
                ilInstance.CLRInstance = clrInstance;

                res = clrInstance.ILInstance;//交给ILRuntime的实例应该为ILInstance

                clrInstance.Awake();//因为Unity调用这个方法时还没准备好所以这里补调一次
            }

            return ILIntepreter.PushObject(ptr, __mStack, res);
        }

        return __esp;
    }

    unsafe static StackObject* GetComponent(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
    {
        //CLR重定向的说明请看相关文档和教程，这里不多做解释
        ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;

        var ptr = __esp - 1;
        //成员方法的第一个参数为this
        GameObject instance = StackObject.ToObject(ptr, __domain, __mStack) as GameObject;
        if (instance == null)
            throw new System.NullReferenceException();
        __intp.Free(ptr);

        var genericArgument = __method.GenericArguments;
        //AddComponent应该有且只有1个泛型参数
        if (genericArgument != null && genericArgument.Length == 1)
        {
            var type = genericArgument[0];
            object res = null;
            if (type is CLRType)
            {
                //Unity主工程的类不需要任何特殊处理，直接调用Unity接口
                res = instance.GetComponent(type.TypeForCLR);
            }
            else
            {
                //因为所有DLL里面的MonoBehaviour实际都是这个Component，所以我们只能全取出来遍历查找
                var clrInstances = instance.GetComponents<MonoBehaviourAdapter.Adaptor>();
                for(int i = 0; i < clrInstances.Length; i++)
                {
                    var clrInstance = clrInstances[i];
                    if (clrInstance.ILInstance != null)//ILInstance为null, 表示是无效的MonoBehaviour，要略过
                    {
                        if (clrInstance.ILInstance.Type == type)
                        {
                            res = clrInstance.ILInstance;//交给ILRuntime的实例应该为ILInstance
                            break;
                        }
                    }
                }
            }

            return ILIntepreter.PushObject(ptr, __mStack, res);
        }

        return __esp;
    }
    }
}
